using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Design;
using System.Globalization;
using System.Windows.Forms;
using EnvDTE;
using Microsoft.VisualStudio.Shell;
using Microsoft.WowAddonStudio.FrameXml.Components;
using Microsoft.WowAddonStudio.FrameXml.Components.Designers;
using Microsoft.WowAddonStudio.FrameXml.Components.Support;
using Microsoft.WowAddonStudio.FrameXml.Serialization;
using Microsoft.WowAddonStudio.FrameXml.Serialization.LayoutFrames;
using Microsoft.WowAddonStudio.FrameXml.Services;
using Color = System.Drawing.Color;
using Font = System.Drawing.Font;

namespace Microsoft.WowAddonStudio.FrameXml.Editor.ToolWindows
{
    public enum LayoutShowState
    {
        Undefine,
        All,
        VirtualOnly,
        RefreshAll
    }

    /// <summary>
    /// Manage visibility state of controls in FrameXmlDesinger.
    /// </summary>
    public class LayoutControlManager : ILayoutControlManager
    {
        private readonly ILayoutControllerHost controllerHost;
        private readonly WindowPane controlToolWindow;
        private readonly List<IDesigner> designers = new List<IDesigner>();
        private readonly IServiceProvider serviceProvider;
        private readonly object syncRoot = new object();
        private IDesignerHost cachedDesignerHost;
        private Events events;
        private bool forceReload;
        private bool initInProgress;
        private IPropertyValueUIService propertyValueUIService;
        private bool selecting;
        private ISelectionService selectionService;
        private WindowEvents windowsEvents;
        private LayoutShowState currentState = LayoutShowState.Undefine;
        private TreeNode currentSetlectedTreeNode;

        /// <summary>
        /// Initializes a new instance of the <see cref="LayoutControlManager"/> class.
        /// </summary>
        /// <param name="provider"></param>
        public LayoutControlManager(IServiceProvider provider)
        {
            if (provider == null)
                throw new ArgumentNullException("provider");

            serviceProvider = provider;
            controlToolWindow = (WindowPane)provider.GetService(typeof(LayoutControllerToolWindow));

            if (controlToolWindow == null)
                throw new ControllerInitializationException("layoutControlToolWindow");

            controllerHost = ((ILayoutControllerHost)controlToolWindow.Window);

            if (controllerHost == null)
                throw new ControllerInitializationException("layoutControlToolWindow");

            TrackSelection = true;

            controllerHost.FrameControlStateChanged += OnFrameControlStateChanged;
            controllerHost.FrameControlSelected += OnFrameControlSelected;
            controllerHost.LayoutStateChanged += OnLayoutStateChanged;
        }

        /// <summary>
        /// Gets or sets a value indicating whether [track selection].
        /// </summary>
        /// <value><c>true</c> if [track selection]; otherwise, <c>false</c>.</value>
        public bool TrackSelection { get; set; }

        #region DTE WindowEvents Implementations

        /// <summary>
        /// Registers handlers for the Activated and Closing events from the text window.
        /// </summary>
        internal void AddWindowEvents()
        {
            var dte = (DTE)serviceProvider.GetService(typeof(DTE));
            events = dte.Events;
            windowsEvents = events.get_WindowEvents(null);
            windowsEvents.WindowActivated += OnWindowActivated;
            windowsEvents.WindowClosing += OnWindowClosing;
        }

        /// <summary>
        /// Occurs when the text window is activated.
        /// </summary>
        /// <param name="gotFocus">The window that received the focus.</param>
        /// <param name="lostFocus">The window that lost the focus.</param>
        public void OnWindowActivated(Window gotFocus, Window lostFocus)
        {
            if (gotFocus != null)
            {
                if (gotFocus.Type.Equals(vsWindowType.vsWindowTypeDocument))
                {
                    //add custom implementation
                }
            }
        }

        /// <summary>
        /// Occurs just before the text window closes.
        /// </summary>
        /// <param name="window">The window that is closing.</param>
        public void OnWindowClosing(Window window)
        {
            //add custom implementation
        }

        #endregion

        #region ILayoutControlManager Members

        /// <summary>
        /// Registers the specified designer host.
        /// </summary>
        /// <param name="designerHost">The designer host.</param>
        /// <returns></returns>
        public bool Register(IDesignerHost designerHost)
        {
            Debug.WriteLine("Entering LayoutControlManager.Register...");

            if (designerHost == null)
                return false;

            if (cachedDesignerHost != designerHost || forceReload)
            {
                DetachDesignerServiceEvents(cachedDesignerHost);

                cachedDesignerHost = designerHost;

                InitializeLayoutTree(cachedDesignerHost);

                AttachDesignerServiceEvents(cachedDesignerHost);

                forceReload = false;
                return true;
            }

            return false;
        }

        /// <summary>
        /// Registers the specified designer host.
        /// </summary>
        /// <param name="designerHost">The designer host.</param>
        /// <returns></returns>
        public bool Unregister(IDesignerHost designerHost)
        {
            //Debug.WriteLine("Entering LayoutControlManager.Register...");
            if (designerHost == null)
                return false;

            if (cachedDesignerHost == designerHost)
            {
                DetachDesignerServiceEvents(cachedDesignerHost);

                cachedDesignerHost = null;

                Reset();

                return true;
            }

            return false;
        }

        /// <summary>
        /// Resumes this instance.
        /// </summary>
        public void Resume()
        {
            forceReload = true;
            Register(cachedDesignerHost);
        }

        /// <summary>
        /// Suspends this instance.
        /// </summary>
        public void Suspend()
        {
            forceReload = false;
        }

        /// <summary>
        /// Resets this instance.
        /// </summary>
        public void Reset()
        {
            controllerHost.LayoutTree.Nodes.Clear();
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing,
        /// or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        public void Edit(IFrameXmlControl control)
        {
            var inheritableObject = control.SerializationObject as InheritableObject;
            if (inheritableObject == null)
                return;

            if (inheritableObject.Virtual)
                ShowSelectedVirtualControl(control);
            else
                ShowAllControls();
        }

        #endregion

        /// <summary>
        /// Initializes the layout tree.
        /// </summary>
        /// <param name="designerHost">The designer host.</param>
        private void InitializeLayoutTree(IDesignerHost designerHost)
        {
            Debug.WriteLine("Entering LayoutControlManager.InitializeLayoutTree...");

            this.cachedDesignerHost = designerHost;

            if (this.currentState == LayoutShowState.All)
            {
                ShowAllControls();
            }
            else if (this.currentState == LayoutShowState.VirtualOnly)
            {
                BuildLayoutTree();

                var virtualControlNode = FindNode(this.controllerHost.LayoutTree.Nodes, "$VirtualControls");
                ShowSelectedVirtualControl(virtualControlNode.Nodes[0]);
            }
            else
            {
                BuildLayoutTree();
            }
        }

        /// <summary>
        /// Initializes the layout tree.
        /// </summary>
        private void BuildLayoutTree()
        {
            TreeView treeView = controllerHost.LayoutTree;

            try
            {
                initInProgress = true;
                treeView.BeginUpdate();
                treeView.Nodes.Clear();

                // Create and add "Controls" node to treeview.
                var rootNode = new TreeNode { Text = "Controls", Name = "$Controls", Checked = true };
                treeView.Nodes.Add(rootNode);

                // Create and add "Virtual Controls" node to treeview.
                var virtualNode = new TreeNode { Text = "Virtual Controls", Name = "$VirtualControls", Checked = true };
                rootNode.Nodes.Add(virtualNode);

                //Add controls to hierarchical view
                ProcessControls(cachedDesignerHost.RootComponent as Control, rootNode);
                
                // Select the Ui node.
                SelectUiNode();

                treeView.ExpandAll();
            }
            finally
            {
                treeView.EndUpdate();
                initInProgress = false;
            }
        }

        /// <summary>
        /// Processes the controls.
        /// </summary>
        /// <param name="component">The component.</param>
        /// <param name="node">The node.</param>
        private void ProcessControls(Control component, TreeNode node)
        {
            var treeNode = new TreeNode(FormatControlName(component)) 
            { 
                ImageIndex = LayoutImageList.GetImageIndex(component.GetType()),
                Tag = component,
                Name = component.Name
            };

            treeNode.SelectedImageIndex = treeNode.ImageIndex;

            bool isVirtual = false;

            var frameControl = component as LayoutFrameControl;
            if (frameControl != null)
            {
                var layoutFrame = frameControl.SerializationObject as LayoutFrame;
                if (layoutFrame != null)
                {
                    layoutFrame.IsInVirtualState = false;

                    treeNode.Checked = !layoutFrame.LayoutHidden;
                    isVirtual = layoutFrame.Virtual;
                    bool isInherited = frameControl.IsInherited;

                    bool isRootParentVirtual = IsRootParentVirtual(node);

                    if (isVirtual || isRootParentVirtual)
                    {
                        layoutFrame.LayoutHidden = true;

                        if (isRootParentVirtual)
                            layoutFrame.LayoutHidden = false;

                        treeNode.NodeFont = new Font(node.TreeView.Font.FontFamily, 8f, FontStyle.Italic);
                        treeNode.ToolTipText = "Virtual control.";
                        treeNode.Checked = false;

                        // Force to reinitialize layout position.
                        frameControl.InitializeVirtualLayoutPosition();
                    }

                    if (isInherited)
                    {
                        treeNode.ForeColor = Color.Gray;
                        treeNode.ToolTipText = "Inherited control.";
                    }
                }
            }
            else
            {
                treeNode.Checked = true;
            }

            if (!isVirtual)
            {
                node.Nodes.Add(treeNode);
            }
            else
            {
                var virtualNode = FindNode(controllerHost.LayoutTree.Nodes, "$VirtualControls");
                virtualNode.Nodes.Add(treeNode);
            }

            //Add child nodes
            if (component.Controls.Count > 0)
                foreach (Control control in component.Controls)
                    ProcessControls(control, treeNode);
        }

        /// <summary>
        /// Called when [layout state changed].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="Microsoft.WowAddonStudio.FrameXml.Editor.ToolWindows.LayoutStateChangedEventArgs"/> instance containing the event data.</param>
        private void OnLayoutStateChanged(object sender, LayoutStateChangedEventArgs e)
        {
            // Reinitialize comoponent hierarchical
            if(this.currentState == LayoutShowState.VirtualOnly)
                ReinitializeComponent(currentSetlectedTreeNode);

            switch (e.State)
            {
                case LayoutShowState.All:
                    ShowAllControls();
                    break;
                case LayoutShowState.RefreshAll:
                    InitializeLayoutTree(cachedDesignerHost);
                    break;
                case LayoutShowState.Undefine:
                    InitializeLayoutTree(cachedDesignerHost);
                    break;
                case LayoutShowState.VirtualOnly:
                    ShowSelectedVirtualControl(e.SelectedNode);
                    break;
                default:
                    throw new NotSupportedException("LayoutShowState");
            }

            // Set actual state and node.
            this.currentState = e.State;
        }

        /// <summary>
        /// Shows all controls except virtual controls.
        /// </summary>
        private void ShowAllControls()
        {
            initInProgress = true;

            // Sets current state
            this.currentState = LayoutShowState.All;

            // Hide all virtual control
            WalkControls((Control)cachedDesignerHost.RootComponent, HideVirtualControlsAction());

            // Reinitialize layout tree
            BuildLayoutTree();

            // Sets the virtual state of Ui control.
            var ui = (UiControl)GetControl("Ui");
            ui.IsInVirtualState = false;

            // Select the Ui node.
            SelectUiNode();

            initInProgress = false;
        }

        /// <summary>
        /// Shows the selected virtual control.
        /// </summary>
        /// <param name="control">The control.</param>
        private void ShowSelectedVirtualControl(IFrameXmlControl control)
        {
            var treeNode = FindNode(controllerHost.LayoutTree.Nodes, control);
            if (treeNode != null)
            {
                controllerHost.LayoutTree.SelectedNode = treeNode;
                this.ShowSelectedVirtualControl(treeNode);
            }
        }

        /// <summary>
        /// Shows the selected virtual control.
        /// </summary>
        private void ShowSelectedVirtualControl(TreeNode selectedNode)
        {
            if (selectedNode == null)
                throw new ArgumentNullException("selectedNode");

            this.currentSetlectedTreeNode = selectedNode;

            initInProgress = true;

            // Sets current state.
            this.currentState = LayoutShowState.VirtualOnly;

            // Hide all controls.
            WalkControls((Control)cachedDesignerHost.RootComponent, HideAllControlAction());

            // Sets the virtual state of Ui control.
            var ui = (UiControl)GetControl("Ui");
            ui.IsInVirtualState = true;

            // Remove all tree nodes from Ui.
            TreeNode treeNode = FindNode(controllerHost.LayoutTree.Nodes, "Ui");
            treeNode.Remove();

            // Find VirtualControls node and clear it.
            treeNode = FindNode(controllerHost.LayoutTree.Nodes, "$VirtualControls");
            treeNode.Nodes.Clear();

            // Add the selected node to VirtualControls.
            selectedNode.Checked = true;
            treeNode.Nodes.Add(selectedNode);

            // Sets the status of selected virtual control.
            var layoutFrame = (LayoutFrame)GetSerializationObject(selectedNode);
            layoutFrame.LayoutHidden = false;
            layoutFrame.IsInVirtualState = true;
            layoutFrame.Locked = false;

            SetStateRecursive((Control)selectedNode.Tag, false);
            SetStateRecursiveInTree(treeNode.Nodes, false);

            // Select selected control in the tree.
            TreeView treeView = controllerHost.LayoutTree;
            treeView.SelectedNode = selectedNode;

            // Force to reinitilaize position of virtual controls.
            ((LayoutFrameControl)selectedNode.Tag).InitializeVirtualLayoutPosition();

            initInProgress = false;
        }

        /// <summary>
        /// Hides all control.
        /// </summary>
        /// <returns></returns>
        private Action<Control> HideAllControlAction()
        {
            return delegate(Control control)
                       {
                           var layoutFrame = GetLayoutFrame(control);
                           if (layoutFrame != null)
                           {
                               layoutFrame.LayoutHidden = true;
                               layoutFrame.IsInVirtualState = false;
                           }
                       };
        }

        /// <summary>
        /// Hides all non virtual control.
        /// </summary>
        /// <returns></returns>
        private Action<Control> HideVirtualControlsAction()
        {
            return delegate(Control control)
                        {
                            var layoutFrame = GetLayoutFrame(control);
                            if (layoutFrame != null)
                            {
                                layoutFrame.LayoutHidden = layoutFrame.Virtual;
                                layoutFrame.IsInVirtualState = false;
                            }
                        };
        }

        /// <summary>
        /// Gets the layout frame.
        /// </summary>
        /// <param name="control">The control.</param>
        /// <returns></returns>
        private LayoutFrame GetLayoutFrame(Control control)
        {
            LayoutFrame layoutFrame = null;

            if (control != null && !(control is UiControl))
            {
                var serializationObject = ((IFrameXmlComponent)control).SerializationObject;
                if (serializationObject is LayoutFrame)
                    layoutFrame = (LayoutFrame)serializationObject;
            }

            return layoutFrame;
        }

        /// <summary>
        /// Attaches the component service events.
        /// </summary>
        /// <param name="designerHost">The designer host.</param>
        private void AttachDesignerServiceEvents(IDesignerHost designerHost)
        {
            if (designerHost != null)
            {
                try
                {
                    //Attach IComponentChangeService events
                    var componentChangeService = designerHost.GetService(typeof (IComponentChangeService)) as IComponentChangeService;
                    if (componentChangeService == null)
                        throw new ControllerInitializationException("componentChangeService");

                    componentChangeService.ComponentAdded += OnComponentAdded;
                    componentChangeService.ComponentRemoved += OnComponentRemoved;
                    componentChangeService.ComponentChanged += OnComponentChanged;

                    //Attach ISelectionService events
                    selectionService = designerHost.GetService(typeof (ISelectionService)) as ISelectionService;
                    if (selectionService != null)
                    {
                        selectionService.SelectionChanged += OnSelectionChanged;
                        selectionService.SelectionChanging += OnSelectionChanging;
                    }

                    //Attach IPropertyValueUIService events
                    propertyValueUIService = designerHost.GetService(typeof (IPropertyValueUIService)) as IPropertyValueUIService;
                    if (propertyValueUIService != null)
                        propertyValueUIService.PropertyUIValueItemsChanged += OnPropertyUIValueItemsChanged;
                }
                catch (Exception e)
                {
                    Trace.WriteLine(e);
                }
            }
        }

        /// <summary>
        /// Called when [selection changing].
        /// </summary>
        /// <param name="sender">The sender as ISelectionService.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void OnSelectionChanging(object sender, EventArgs e)
        {
            if (selectionService != null)
            {
                ICollection selectedComponents = selectionService.GetSelectedComponents();
                var components = new List<IComponent>();

                foreach (IComponent component in selectedComponents)
                {
                    var frameXmlComponent = component as IFrameXmlComponent;
                    if (frameXmlComponent != null)
                    {
                        if (frameXmlComponent.GetType() == typeof(GameTooltipComponent) || frameXmlComponent.GetType() == typeof(FontComponent))
                        {
                            // Font and GameTooltip always selectable
                            components.Add(frameXmlComponent);
                        }
                        else
                        {

                            var layoutFrame = frameXmlComponent.SerializationObject as LayoutFrame;

                            if (layoutFrame != null)
                            {


                                TreeNode node;
                                switch (currentState)
                                {
                                    case LayoutShowState.All:
                                    case LayoutShowState.RefreshAll:
                                    case LayoutShowState.Undefine:
                                        // Find node in the "Ui" tree.
                                        node = FindNode(controllerHost.LayoutTree.Nodes[0].Nodes[1], component.Site.Name);
                                        if (node != null && !layoutFrame.LayoutHidden && !layoutFrame.Virtual)
                                            components.Add(frameXmlComponent);
                                        break;
                                    case LayoutShowState.VirtualOnly:
                                        // Find node in the "Virtual controls" tree.
                                        node = FindNode(controllerHost.LayoutTree.Nodes[0].Nodes[0], component.Site.Name);
                                        if (node != null)
                                            components.Add(frameXmlComponent);
                                        break;
                                    default:
                                        throw new NotSupportedException("LayoutShowState");
                                }
                            }
                        }
                    }
                    else
                    {
                        components.Add(frameXmlComponent);
                    }
                }
                
                if (components.Count != selectedComponents.Count)
                    selectionService.SetSelectedComponents(components);
            }
        }

        /// <summary>
        /// Called when [selection changed].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void OnSelectionChanged(object sender, EventArgs e)
        {
            if (selectionService != null)
            {
                try
                {
                    selecting = true;
                    ICollection selectedComponents = selectionService.GetSelectedComponents();
                    foreach (object component in selectedComponents)
                    {
                        var ctrl = component as Control;
                        if (ctrl != null)
                        {
                            TreeNode node = FindNode(controllerHost.LayoutTree.Nodes[0], ctrl.Name);
                            if (node != null)
                                controllerHost.LayoutTree.SelectedNode = node;
                        }
                    }
                }
                finally
                {
                    selecting = false;
                }
            }
        }

        /// <summary>
        /// Called when [component changed].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.ComponentModel.Design.ComponentChangedEventArgs"/> instance containing the event data.</param>
        private void OnComponentChanged(object sender, ComponentChangedEventArgs e)
        {
            //Refresh control tree if control collection changed
            if (e.Member.DisplayName == "Controls")
            {
                var control = e.Component as Control;
                if (control != null)
                {
                    try
                    {
                        RefreshNode(FindNode(controllerHost.LayoutTree.Nodes[0], control.Name), control);
                    }
                    catch (Exception ex)
                    {
                        Trace.WriteLine(ex);
                    }
                }
            }
            //Refresh control name if changed
            else if (e.Member.DisplayName == "Name")
            {
                if (!initInProgress)
                {
                    Control control = FindControl(cachedDesignerHost.RootComponent as Control, (string)e.OldValue);
                    if (control != null)
                    {
                        initInProgress = true;

                        try
                        {
                            var newName = (string)e.NewValue;
                            string expandName = newName;
                            var frameXmlControl = control as IFrameXmlControl;

                            //Rename SerializationObject to new name
                            if (frameXmlControl != null)
                            {
                                var namedObject = frameXmlControl.SerializationObject as InheritableObject;
                                if (namedObject != null)
                                {
                                    if (frameXmlControl.HasParent)
                                        expandName = newName.Replace("$parent", frameXmlControl.Parent.Name);

                                    namedObject.Name = newName;
                                }
                            }
                            //Rename control node in layout tree
                            TreeNode node = FindNode(controllerHost.LayoutTree.Nodes[0], (string)e.OldValue);
                            if (node != null)
                            {
                                node.Text = FormatControlName(control, expandName);
                                node.Name = expandName;
                            }

                            //Rename Site to new name
                            if (control.Name != newName && control.Site != null)
                            {
                                control.Site.Name = control.Name = expandName;
                            }
                        }
                        catch (Exception ex)
                        {
                            Trace.WriteLine(ex);
                            var service = (IFrameXmlDesignerService)cachedDesignerHost.GetService(typeof(IFrameXmlDesignerService));
                            if (service != null)
                            {
                                service.NotifyError(ex.Message);
                            }
                        }
                        finally
                        {
                            initInProgress = false;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Called when [initialized new component].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="Microsoft.WowAddonStudio.FrameXml.Components.Designers.FrameDesignerEventArgs"/> instance containing the event data.</param>
        private void OnInitializedNewComponent(object sender, FrameDesignerEventArgs e)
        {
            if (cachedDesignerHost != null && e.NewComponent != null)
            {
                if (e.NewComponent is Control)
                {
                    // Add control
                    AddControlToLayoutTree((Control)e.NewComponent, e.Parent);

                    InitializeLayoutTree(cachedDesignerHost);
                }
                else
                {
                    // Add component
                    IFrameXmlComponent frameXmlComponent = null;

                    if (e.NewComponent is FontComponent)
                        frameXmlComponent = (FontComponent)e.NewComponent;
                    else if (e.NewComponent is GameTooltipComponent)
                        frameXmlComponent = (GameTooltipComponent)e.NewComponent;

                    if (frameXmlComponent != null && cachedDesignerHost.RootComponent is Control)
                    {
                        var rootComponent = (IFrameXmlComponent)cachedDesignerHost.RootComponent;
                        var rootComponentContainer = (ISerializationObjectContainer)rootComponent.SerializationObject;
                        rootComponentContainer.Add(frameXmlComponent.SerializationObject);
                    }
                }

                var parentComponent = e.Parent as IFrameXmlComponent;
                var childComponent = e.NewComponent as IFrameXmlComponent;

                if (childComponent != null)
                {
                    if (parentComponent != null)
                    {
                        var childLayoutFrame = childComponent.SerializationObject as LayoutFrame;
                        var parentLayoutFrame = parentComponent.SerializationObject as LayoutFrame;

                        // Sets the parent of the new component.
                        if (childLayoutFrame != null && parentLayoutFrame != null)
                            childLayoutFrame.ParentFrame = parentLayoutFrame;
                    }

                    // Initialize the name of component.
                    childComponent.InitializeName();
                }

                SetDirtyFlag(e.NewComponent.Site);
            }
        }

        /// <summary>
        /// Detaches the component service events.
        /// </summary>
        /// <param name="designerHost">The designer host.</param>
        private void DetachDesignerServiceEvents(IDesignerHost designerHost)
        {
            Debug.WriteLine("Entering LayoutControlManager.DetachDesignerServiceEvents...");
            if (designerHost != null)
                try
                {
                    var componentChangeService = designerHost.GetService(typeof(IComponentChangeService)) as IComponentChangeService;
                    if (componentChangeService != null)
                    {
                        componentChangeService.ComponentAdded -= OnComponentAdded;
                        componentChangeService.ComponentRemoved -= OnComponentRemoved;
                        componentChangeService.ComponentChanged -= OnComponentChanged;
                    }

                    if (selectionService != null)
                    {
                        selectionService.SelectionChanged -= OnSelectionChanged;
                        selectionService.SelectionChanging -= OnSelectionChanging;
                    }
                    selectionService = null;

                    if (propertyValueUIService != null)
                    {
                        propertyValueUIService.PropertyUIValueItemsChanged += OnPropertyUIValueItemsChanged;
                    }
                    propertyValueUIService = null;
                }
                catch (Exception e)
                {
                    Trace.WriteLine(e);
                }
        }

        /// <summary>
        /// Called when [property UI value items changed].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void OnPropertyUIValueItemsChanged(object sender, EventArgs e)
        {
            if (!initInProgress && propertyValueUIService != null && cachedDesignerHost != null)
            {
                initInProgress = true;
                try
                {
                    controllerHost.LayoutTree.BeginUpdate();
                    //Refresh the State and Font properties of nodes
                    WalkControls(cachedDesignerHost.RootComponent as Control,
                                 ctrl =>
                                 {
                                     var frameControl = ctrl as LayoutFrameControl;
                                     if (frameControl != null)
                                     {
                                         var layoutFrame = frameControl.SerializationObject as LayoutFrame;
                                         if (layoutFrame != null)
                                         {
                                             TreeNode node = FindNode(controllerHost.LayoutTree.Nodes[0], ctrl.Name);

                                             if (node != null)
                                             {
                                                 node.Checked = !layoutFrame.LayoutHidden;
                                                 FontStyle fontStyle = FontStyle.Regular;
                                                 if (layoutFrame.Virtual)
                                                     fontStyle = FontStyle.Italic;
                                                 node.NodeFont = new Font(node.TreeView.Font.FontFamily, 8f, fontStyle);
                                             }
                                             else
                                             {
                                                 RefreshNode(controllerHost.LayoutTree.Nodes[0], ctrl.Parent);
                                             }
                                         }
                                     }
                                 }
                        );
                }
                finally
                {
                    InitializeLayoutTree(cachedDesignerHost);

                    initInProgress = false;
                    controllerHost.LayoutTree.EndUpdate();
                }
            }
        }

        /// <summary>
        /// Occurs when a component has been added.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnComponentAdded(object sender, ComponentEventArgs e)
        {
            if (cachedDesignerHost != null && e.Component != null)
            {
                IDesigner designer = cachedDesignerHost.GetDesigner(e.Component);
                if (designer != null && designer is ILayoutFrameDesignerEvents && !designers.Contains(designer))
                {
                    designers.Add(designer);
                    ((ILayoutFrameDesignerEvents)designer).InitializedNewComponent += OnInitializedNewComponent;
                }
            }
        }

        /// <summary>
        /// Occurs when a component has been removed.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnComponentRemoved(object sender, ComponentEventArgs e)
        {
            if (cachedDesignerHost != null && e.Component != null)
            {
                if (e.Component is Control)
                {
                    // Remove control
                    RemoveControlToLayoutTree((Control)e.Component);
                }
                else
                {
                    // Remove component.
                    IFrameXmlComponent frameXmlComponent = null;

                    if (e.Component is FontComponent)
                        frameXmlComponent = (FontComponent)e.Component;
                    else if (e.Component is GameTooltipComponent)
                        frameXmlComponent = (GameTooltipComponent)e.Component;

                    if (frameXmlComponent != null && cachedDesignerHost.RootComponent is Control)
                    {
                        var rootComponent = (IFrameXmlComponent)cachedDesignerHost.RootComponent;
                        var rootComponentContainer = (ISerializationObjectContainer)rootComponent.SerializationObject;
                        rootComponentContainer.Remove(frameXmlComponent.SerializationObject);
                    }
                }

                // Unregister inheritable object.
                var inheritableObect = ((IFrameXmlComponent)e.Component).SerializationObject as InheritableObject;
                if (inheritableObect != null)
                    inheritableObect.UnregisterObject();

                SetDirtyFlag(e.Component.Site);
            }
        }

        /// <summary>
        /// Adds the control to layout tree.
        /// </summary>
        /// <param name="newControl">The new control.</param>
        /// <param name="parentControl">The parent control.</param>
        /// <returns></returns>
        private void AddControlToLayoutTree(Control newControl, Control parentControl)
        {
            TreeView treeView = controllerHost.LayoutTree;
            AddControlToLayoutTree(newControl, parentControl, treeView.Nodes[0]);
        }

        /// <summary>
        /// Adds the control to layout tree.
        /// </summary>
        /// <param name="newControl">The new control.</param>
        /// <param name="parentControl">The parent.</param>
        /// <param name="rootNode">The root node.</param>
        /// <returns></returns>
        private TreeNode AddControlToLayoutTree(Control newControl, Control parentControl, TreeNode rootNode)
        {
            if (newControl == null)
                return null;

            if (parentControl == null)
                throw new ArgumentNullException("parentControl");

            TreeView treeView = controllerHost.LayoutTree;

            try
            {
                initInProgress = true;
                treeView.BeginUpdate();


                TreeNode treeNode = FindNode(rootNode, parentControl.Name);
                if (FindNodeInNode(treeNode, newControl.Name) == null)
                {
                    if (treeNode != null)
                    {
                        TreeNode node = treeNode.Nodes.Add(FormatControlName(newControl));
                        node.ImageIndex = LayoutImageList.GetImageIndex(newControl.GetType());
                        node.SelectedImageIndex = node.ImageIndex;
                        node.Name = newControl.Name;
                        node.Checked = true;
                        treeNode.ExpandAll();
                        return node;
                    }
                }
            }
            finally
            {
                treeView.EndUpdate();
                initInProgress = false;
            }
            return null;
        }

        /// <summary>
        /// Removes the control to layout tree.
        /// </summary>
        /// <param name="control">The control.</param>
        private void RemoveControlToLayoutTree(Control control)
        {
            TreeView treeView = controllerHost.LayoutTree;
            try
            {
                initInProgress = true;
                treeView.BeginUpdate();

                if (control != null)
                {
                    TreeNode treeNode = FindNode(treeView.Nodes[0], control.Name);
                    if (treeNode != null)
                    {
                        treeView.Nodes.Remove(treeNode);
                    }
                }
            }
            finally
            {
                treeView.EndUpdate();
                initInProgress = false;
            }
        }

        /// <summary>
        /// Adds the sub controls.
        /// </summary>
        /// <param name="rootNode">The root node.</param>
        /// <param name="control">The control.</param>
        private void AddSubControls(TreeNode rootNode, Control control)
        {
            foreach (Control ctrl in control.Controls)
            {
                TreeNode foundNode = null;
                foreach (TreeNode treeNode in rootNode.Nodes)
                {
                    if (treeNode.Name == ctrl.Name)
                    {
                        foundNode = treeNode;
                        break;
                    }
                }
                if (foundNode == null)
                {
                    TreeNode newTreeNode = AddControlToLayoutTree(ctrl, ctrl.Parent, rootNode);
                    if (newTreeNode != null)
                    {
                        AddSubControls(newTreeNode, ctrl);
                    }
                }
            }
        }

        /// <summary>
        /// Refreshes the node.
        /// </summary>
        /// <param name="node">The node.</param>
        /// <param name="control">The control.</param>
        private void RefreshNode(TreeNode node, Control control)
        {
            if (node != null)
            {
                try
                {
                    controllerHost.LayoutTree.BeginUpdate();

                    if (control.Controls.Count == 0 && node.Nodes != null)
                    {
                        node.Nodes.Clear();
                    }
                    if (node.Nodes != null && control.Controls.Count != node.Nodes.Count)
                    {
                        TreeNode rootNode = FindNode(node, control.Name);
                        if (rootNode != null)
                        {
                            if (control.Controls.Count > node.Nodes.Count)
                            {
                                //Add moved control and its children
                                AddSubControls(rootNode, control);
                            }
                            else if (control.Controls.Count < node.Nodes.Count)
                            {
                                //Remove moved control
                                for (int index = rootNode.Nodes.Count - 1; index >= 0; index--)
                                {
                                    TreeNode treeNode = rootNode.Nodes[index];
                                    Control foundControl = null;
                                    foreach (Control ctrl in control.Controls)
                                    {
                                        if (treeNode.Name == ctrl.Name)
                                        {
                                            foundControl = ctrl;
                                            break;
                                        }
                                    }
                                    if (foundControl == null)
                                    {
                                        treeNode.Remove();
                                    }
                                }
                            }
                        }
                    }
                }
                finally
                {
                    controllerHost.LayoutTree.EndUpdate();
                }
            }
        }

        /// <summary>
        /// Called when [frame control state changed].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="Microsoft.WowAddonStudio.FrameXml.Editor.ToolWindows.FrameControlStateChangedEventArgs"/> instance containing the event data.</param>
        private void OnFrameControlStateChanged(object sender, FrameControlStateChangedEventArgs e)
        {
            if (!initInProgress)
            {
                bool hiddenStateSetted = SetControlHiddenState(e.ControlName, e.State);

                if (!hiddenStateSetted || IsRootParentVirtual(e.SelectedNode))
                    e.Cancel = true;
            }
        }

        /// <summary>
        /// Called when [frame control selected].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="Microsoft.WowAddonStudio.FrameXml.Editor.ToolWindows.FrameControlSelectedEventArgs"/> instance containing the event data.</param>
        private void OnFrameControlSelected(object sender, FrameControlSelectedEventArgs e)
        {
            if (!selecting)
            {
                if (TrackSelection && selectionService != null && cachedDesignerHost != null)
                {
                    if (!string.IsNullOrEmpty(e.ControlName))
                    {
                        Control control = FindControl(cachedDesignerHost.RootComponent as Control, e.ControlName);
                        if (control != null)
                            selectionService.SetSelectedComponents(new object[] { control });
                    }
                }
            }
        }

        /// <summary>
        /// Sets the hidden state of the control.
        /// </summary>
        /// <param name="controlName">Name of the control.</param>
        /// <param name="hidden">if set to <c>true</c> [hidden].</param>
        /// <returns></returns>
        private bool SetControlHiddenState(string controlName, bool hidden)
        {
            bool hiddenStateSetted = false;

            if (cachedDesignerHost != null && !initInProgress)
            {
                initInProgress = true;
                lock (syncRoot)
                {
                    try
                    {
                        var frameControl = FindControl(cachedDesignerHost.RootComponent as Control, controlName) as LayoutFrameControl;

                        if (frameControl != null)
                        {
                            var layoutFrame = frameControl.SerializationObject as LayoutFrame;
                            if (layoutFrame != null && !layoutFrame.Virtual)
                            {
                                layoutFrame.LayoutHidden = hidden;

                                // Forces refresh commmand.
                                frameControl.Refresh();

                                hiddenStateSetted = true;

                                //Set subitem's visibility state
                                SetStateRecursive(frameControl, hidden);
                                SetStateRecursiveInTree(FindNode(controllerHost.LayoutTree.Nodes[0], controlName).Nodes, hidden);
                            }
                        }
                    }
                    finally
                    {
                        initInProgress = false;
                    }
                }
            }

            return hiddenStateSetted;
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing,
        /// or resetting unmanaged resources.
        /// </summary>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                designers.ForEach(designer =>
                {
                    ((ILayoutFrameDesignerEvents)designer).
                        InitializedNewComponent -= OnInitializedNewComponent;
                });
                designers.Clear();

                if (controllerHost != null)
                {
                    controllerHost.FrameControlStateChanged -= OnFrameControlStateChanged;
                    controllerHost.FrameControlSelected -= OnFrameControlSelected;
                    controllerHost.LayoutStateChanged -= OnLayoutStateChanged;
                }

                DetachDesignerServiceEvents(cachedDesignerHost);
                cachedDesignerHost = null;

                try
                {
                    if (windowsEvents != null)
                    {
                        windowsEvents.WindowActivated -= OnWindowActivated;
                        windowsEvents.WindowClosing -= OnWindowClosing;
                    }
                }
                catch (Exception ex)
                {
                    Trace.WriteLine(ex);
                }
            }
        }

        /// <summary>
        /// Sets the dirty flag.
        /// </summary>
        /// <param name="site">The site.</param>
        private void SetDirtyFlag(ISite site)
        {
            if (site == null)
                throw new ArgumentNullException("site");

            var framXmlDesignService = ((IFrameXmlDesignerService)site.GetService(typeof(IFrameXmlDesignerService)));
            if (framXmlDesignService != null)
                framXmlDesignService.SetDirty(true);
        }

        #region Node and Component Helper Functions

        /// <summary>
        /// Walks the control collection and executes the specified action delegate.
        /// </summary>
        /// <param name="node">The component.</param>
        /// <param name="action">The action.</param>
        public void WalkNodes(TreeNode node, Action<TreeNode> action)
        {
            if (node != null)
            {
                action(node);
                if (node.Nodes.Count > 0)
                {
                    foreach (TreeNode treeNode in node.Nodes)
                    {
                        WalkNodes(treeNode, action);
                    }
                }
            }
        }

        /// <summary>
        /// Walks the control collection and executes the specified action delegate.
        /// </summary>
        /// <param name="component">The component.</param>
        /// <param name="action">The action.</param>
        public void WalkControls(Control component, Action<Control> action)
        {
            if (component != null)
            {
                action(component);
                if (component.Controls.Count > 0)
                {
                    foreach (Control ctrl in component.Controls)
                    {
                        WalkControls(ctrl, action);
                    }
                }
            }
        }

        /// <summary>
        /// Sets the satet recursive.
        /// </summary>
        /// <param name="component">The component.</param>
        /// <param name="hidden">if set to <c>true</c> [hidden].</param>
        private void SetStateRecursive(Control component, bool hidden)
        {
            var frameControl = component as LayoutFrameControl;
            if (frameControl != null)
            {
                var layoutFrame = frameControl.SerializationObject as LayoutFrame;
                if (layoutFrame != null)
                {
                    layoutFrame.LayoutHidden = hidden;
                    layoutFrame.IsInVirtualState = currentState == LayoutShowState.VirtualOnly;
                    layoutFrame.Locked = currentState != LayoutShowState.VirtualOnly;
                }
            }
            if (component.Controls.Count > 0)
            {
                foreach (Control control in component.Controls)
                {
                    SetStateRecursive(control, hidden);
                }
            }
        }

        /// <summary>
        /// Sets the satet recursive in tree.
        /// </summary>
        /// <param name="nodes">The node.</param>
        /// <param name="hidden">if set to <c>true</c> [hidden].</param>
        private static void SetStateRecursiveInTree(TreeNodeCollection nodes, bool hidden)
        {
            foreach (TreeNode node in nodes)
            {
                node.Checked = !hidden;
                if (node.Nodes.Count > 0)
                {
                    SetStateRecursiveInTree(node.Nodes, hidden);
                }
            }
        }

        /// <summary>
        /// Finds the node.
        /// </summary>
        /// <param name="nodes">The nodes.</param>
        /// <param name="nodeName">Name of the node.</param>
        /// <returns></returns>
        public TreeNode FindNode(TreeNodeCollection nodes, string nodeName)
        {
            TreeNode treeNode = null;
            if (nodes != null)
            {
                if (nodes.Count > 0)
                {
                    foreach (TreeNode subNode in nodes)
                    {
                        if (subNode.Name == nodeName)
                        {
                            treeNode = subNode;
                            return treeNode;
                        }
                        treeNode = FindNode(subNode.Nodes, nodeName);
                        if (treeNode != null)
                            return treeNode;
                    }
                }
            }
            return treeNode;
        }

        /// <summary>
        /// Finds the node.
        /// </summary>
        /// <param name="nodes">The nodes.</param>
        /// <param name="tag"></param>
        /// <returns></returns>
        public TreeNode FindNode(TreeNodeCollection nodes, object tag)
        {
            TreeNode treeNode = null;
            if (nodes != null)
            {
                if (nodes.Count > 0)
                {
                    foreach (TreeNode subNode in nodes)
                    {
                        if (subNode.Tag == tag)
                        {
                            treeNode = subNode;
                            return treeNode;
                        }
                        treeNode = FindNode(subNode.Nodes, tag);
                        if (treeNode != null)
                            return treeNode;
                    }
                }
            }
            return treeNode;
        }
        /// <summary>
        /// Finds the node in node.
        /// </summary>
        /// <param name="node">The node.</param>
        /// <param name="nodeName">Name of the node.</param>
        /// <returns></returns>
        public TreeNode FindNodeInNode(TreeNode node, string nodeName)
        {
            if (node == null)
                return null;

            if (node.Name == nodeName)
            {
                return node;
            }
            if (node.Nodes.Count > 0)
            {
                foreach (TreeNode subNode in node.Nodes)
                {
                    if (subNode.Name == nodeName)
                    {
                        return subNode;
                    }
                }
            }
            return null;
        }

        /// <summary>
        /// Finds the node.
        /// </summary>
        /// <param name="node">The node.</param>
        /// <param name="nodeName">Name of the control.</param>
        /// <returns></returns>
        public TreeNode FindNode(TreeNode node, string nodeName)
        {
            if (node == null)
                throw new ArgumentNullException("node");

            TreeNode treeNode = null;
            if (node.Name == nodeName)
            {
                treeNode = node;
            }
            else
            {
                if (node.Nodes.Count > 0)
                {
                    foreach (TreeNode subNode in node.Nodes)
                    {
                        treeNode = FindNode(subNode, nodeName);
                        if (treeNode != null)
                            return treeNode;
                    }
                }
            }
            return treeNode;
        }

        /// <summary>
        /// Finds the control.
        /// </summary>
        /// <param name="component">The component.</param>
        /// <param name="controlName">Name of the control.</param>
        /// <returns></returns>
        public Control FindControl(Control component, string controlName)
        {
            Control control = null;
            if (component != null)
            {
                if (component.Name == controlName)
                {
                    control = component;
                }
                else
                {
                    if (component.Controls.Count > 0)
                    {
                        foreach (Control ctrl in component.Controls)
                        {
                            control = FindControl(ctrl, controlName);
                            if (control != null)
                                return control;
                        }
                    }
                }
            }
            return control;
        }

        /// <summary>
        /// Finds the parent.
        /// </summary>
        /// <param name="controlToFind">The control to find.</param>
        /// <returns></returns>
        public Control FindParent(Control controlToFind)
        {
            Control parent = FindParent(cachedDesignerHost.RootComponent as Control, controlToFind);

            if (parent == controlToFind || parent == null)
            {
                parent = cachedDesignerHost.RootComponent as Control;
            }

            return parent;
        }

        /// <summary>
        /// Finds the parent. If the parent is the RootComponent 
        /// then return the controlToFind as result.
        /// </summary>
        /// <param name="component">The component.</param>
        /// <param name="controlToFind">The control to find.</param>
        /// <returns></returns>
        public Control FindParent(Control component, Control controlToFind)
        {
            Control control = null;
            if (component != null && controlToFind != null)
            {
                if (component.Name == controlToFind.Name)
                {
                    control = component;
                }
                else
                {
                    if (component.Controls.Count > 0)
                    {
                        foreach (Control ctrl in component.Controls)
                        {
                            control = FindParent(ctrl, controlToFind);
                            if (control == controlToFind)
                                return ctrl;
                        }
                    }
                }
            }
            return control;
        }

        /// <summary>
        /// Formats the name of the control.
        /// </summary>
        /// <param name="control">The control.</param>
        /// <param name="name">The name.</param>
        /// <returns></returns>
        private static string FormatControlName(Control control, string name)
        {
            if (control != null)
            {
                return String.Format(CultureInfo.InvariantCulture, "{0} [{1}]", name,
                                     ((IFrameXmlControl)control).SerializationObject.GetType().Name);
            }
            return "Unknown";
        }

        /// <summary>
        /// Formats the name of the control.
        /// </summary>
        /// <param name="control">The control.</param>
        /// <returns></returns>
        private static string FormatControlName(Control control)
        {
            return FormatControlName(control, control.Name);
        }

        /// <summary>
        /// Determines whether [is root parent virtual] [the specified tree node].
        /// </summary>
        /// <param name="treeNode">The tree node.</param>
        /// <returns>
        /// 	<c>true</c> if [is root parent virtual] [the specified tree node]; otherwise, <c>false</c>.
        /// </returns>
        private static bool IsRootParentVirtual(TreeNode treeNode)
        {
            bool isVirtual = false;

            if (treeNode != null)
            {

                TreeNode node = treeNode;
                TreeNode prevNode = null;

                while (node.Name != "$VirtualControls")
                {
                    if (node.Name == "$Controls")
                    {
                        prevNode = null;
                        break;
                    }

                    prevNode = node;
                    node = node.Parent;
                }

                if (prevNode != null)
                {
                    var frameControl = prevNode.Tag as LayoutFrameControl;
                    if (frameControl != null)
                    {
                        var layoutFrame = frameControl.SerializationObject as LayoutFrame;
                        if (layoutFrame != null)
                            isVirtual = layoutFrame.Virtual;
                    }
                }
            }

            return isVirtual;
        }

        /// <summary>
        /// Selects the UI node.
        /// </summary>
        private void SelectUiNode()
        {
            TreeNode treeNode = FindNode(controllerHost.LayoutTree.Nodes, "Ui");
            if (controllerHost != null && treeNode != null)
                controllerHost.LayoutTree.SelectedNode = treeNode;
        }

        /// <summary>
        /// Reinitialize comoponent hierarchical
        /// </summary>
        private void ReinitializeComponent(TreeNode treeNode)
        {
            var component = (IFrameXmlComponent) treeNode.Tag;

            foreach (IFrameXmlComponent frameXmlComponent in this.cachedDesignerHost.Container.Components)
            {
                if (frameXmlComponent is LayoutFrameControl)
                {
                    var serializationObject = ((LayoutFrameControl) frameXmlComponent).SerializationObject;

                    if (serializationObject is InheritableObject)
                    {
                        var inherits = ((InheritableObject) serializationObject).Inherits;

                        if (inherits == component.Name)
                        {
                            var service = (FrameXmlComponentFactory) this.cachedDesignerHost.GetService(typeof (FrameXmlComponentFactory));
                            service.ReinitializeComponent(frameXmlComponent);
                            break;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Gets the IFrameXmlControl.
        /// </summary>
        /// <param name="name">Name of the node.</param>
        /// <returns></returns>
        private IFrameXmlControl GetControl(string name)
        {
            TreeNode treeNode = FindNode(controllerHost.LayoutTree.Nodes, name);

            if (treeNode == null)
                throw new NullReferenceException("treeNode");

            return (IFrameXmlControl) treeNode.Tag;
        }

        /// <summary>
        /// Gets the serialization object.
        /// </summary>
        /// <param name="treeNode">The tree node.</param>
        /// <returns></returns>
        private SerializationObject GetSerializationObject(TreeNode treeNode)
        {
            if (treeNode == null)
                throw new ArgumentNullException("treeNode");

            return ((IFrameXmlComponent) treeNode.Tag).SerializationObject;
        }

        #endregion
    }
}